using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.IO;
using System.IO.Pipes;
using System.Runtime.InteropServices;
using System.Text;

//todo: multiple instance usability

namespace DxDeviceCtrl
{
    //use shm to communicate to dinput8.dll
    internal class VkDxHijack
    {
        public int Reqcounter;
        private Collection<StreamWriter> KeybdPipeSwCollection;
        private Collection<StreamWriter> MousePipeSwCollection;
        //merge from MouseCtrl
        private StreamWriter MousePosPipeSW;

        public VkDxHijack()
        {
            KeybdPipeSwCollection = new Collection<StreamWriter>();
            MousePipeSwCollection = new Collection<StreamWriter>();


            var id = 0;
            bool failed = false;
            for (id = 0; false == failed; ++id)
            {
                var KeybdPipe = new NamedPipeClientStream(".", "DxKeybdPipe" + id.ToString(), PipeDirection.Out);
                try
                {
                    KeybdPipe.Connect(500);
                    if (true == KeybdPipe.IsConnected)
                    {
                        var KeybdPipeSW = new StreamWriter(KeybdPipe);
                        KeybdPipeSW.AutoFlush = true;
                        KeybdPipeSwCollection.Add(KeybdPipeSW);
                    }
                    else
                    {
                        failed = true;
                    }
                }
                catch (Exception e)
                {
                    failed = true;
                }
            }
            failed = false;

            for (id = 0; false == failed; ++id)
            {
                var MousePipe = new NamedPipeClientStream(".", "DxMousePipe" + id.ToString(), PipeDirection.Out);
                try
                {
                    MousePipe.Connect(500);

                    if (true == MousePipe.IsConnected)
                    {
                        var mousePipeSw = new StreamWriter(MousePipe);
                        mousePipeSw.AutoFlush = true;
                        MousePipeSwCollection.Add(mousePipeSw);
                    }
                    else
                    {
                        failed = true;
                    }

                }
                catch (Exception e)
                {
                    failed = true;
                }
            }

            //below is for mouse move but no multiple instance support

            var npipe = new NamedPipeClientStream(".", "mGetCursorPosServerPipe0", PipeDirection.Out);

            try
            {
                npipe.Connect(500);

                if (true == npipe.IsConnected)
                {
                    MousePosPipeSW = new StreamWriter(npipe);
                    MousePosPipeSW.AutoFlush = true;
                }

            }
            catch { }

        }

        private void Send(IList<DirectInputKbdKey> iKeys)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var iKey in iKeys)
            {
                sb.AppendFormat("{0}", (ushort)iKey);
            }
            foreach (var streamWriter in KeybdPipeSwCollection)
            {
                streamWriter.Write(sb.ToString());
            }
        }

        private void Send(DirectInputKbdKey iKey)
        {
            var ik = new[] { iKey };
            Send(ik);
        }

        private void Send(dxMouseButton but)
        {
            var sb = new StringBuilder();
            if (but == dxMouseButton.Left)
            {
                sb.Append("L");
            }
            if (but == dxMouseButton.Right)
            {
                sb.Append("R");
            }
            foreach (var streamWriter in MousePipeSwCollection)
            {
                streamWriter.Write(sb.ToString());
            }
        }
        private void _moveTo(int x, int y)
        {
            MousePosPipeSW.Write(string.Format("set {0} {1}", x, y));
        }

        //Related to process
        private void _moveTo(IntPtr hwnd, int x, int y)
        {
            int mx = 0, my = 0;
            GetWindowPos(hwnd, ref mx, ref my);
            MoveTo(mx + x, my + y);
        }
        private void _release()
        {
            MousePosPipeSW.Write("clr");
        }

        private static IntPtr CreateLParam(int LoWord, int HiWord)
        {
            return (IntPtr)((HiWord << 16) | (LoWord & 0xffff));
        }



        private static class User32
        {
            [DllImport("user32.dll")]
            public static extern IntPtr FindWindow(String sClassName, String sAppName);
            [DllImport("user32", ExactSpelling = true, SetLastError = true)]
            internal static extern int MapWindowPoints(IntPtr hWndFrom, IntPtr hWndTo, [In, Out] ref Rectangle rect, [MarshalAs(UnmanagedType.U4)] int cPoints);
            [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
            public static extern IntPtr GetParent(IntPtr hWnd);
        }


        #region Methods keyboard/mouse

        public void MoveTo(int x, int y)
        {
            _moveTo(x, y);
        }
        public void MoveTo(IntPtr hwnd, int x, int y)
        {
            if (hwnd == IntPtr.Zero)
            {
                return;
            }
            int mx = 0, my = 0;
            GetWindowPos(hwnd, ref mx, ref my);

            _moveTo(mx + x, my + y);
        }
        public void Release()
        {
            _release();
        }
        public void LeftClick()
        {
            Send(dxMouseButton.Left);
        }
        public void RightClick()
        {
            Send(dxMouseButton.Right);
        }

        public void Keybd(IList<DirectInputKbdKey> iKeys)
        {
            Send(iKeys);
        }

        #region Window BasePoint Functions
        public IntPtr FindWindow(string sClassname, string sAppName)
        {
            return User32.FindWindow(sClassname, sAppName);
        }
        public void GetWindowPos(IntPtr hWnd, ref int x, ref int y)
        {
            if (hWnd == IntPtr.Zero)
            {
                return;
            }
            IntPtr hWndParent = User32.GetParent(hWnd);
            var rect = new Rectangle();

            User32.MapWindowPoints(hWnd, hWndParent, ref rect, 1);
            var point = rect.Location;
            x = point.X;
            y = point.Y;
        }
        #endregion
        #endregion

        #region Constants

        public enum dxMouseButton
        {
            Left,
            Right
        }

        public enum DirectInputKbdKey
            : ushort
        {
            DIK_ESCAPE = 0x01,
            DIK_1 = 0x02,
            DIK_2 = 0x03,
            DIK_3 = 0x04,
            DIK_4 = 0x05,
            DIK_5 = 0x06,
            DIK_6 = 0x07,
            DIK_7 = 0x08,
            DIK_8 = 0x09,
            DIK_9 = 0x0A,
            DIK_0 = 0x0B,
            DIK_MINUS = 0x0C,    /* - on main keyboard */
            DIK_EQUALS = 0x0D,
            DIK_BACK = 0x0E,    /* backspace */
            DIK_TAB = 0x0F,
            DIK_Q = 0x10,
            DIK_W = 0x11,
            DIK_E = 0x12,
            DIK_R = 0x13,
            DIK_T = 0x14,
            DIK_Y = 0x15,
            DIK_U = 0x16,
            DIK_I = 0x17,
            DIK_O = 0x18,
            DIK_P = 0x19,
            DIK_LBRACKET = 0x1A,
            DIK_RBRACKET = 0x1B,
            DIK_RETURN = 0x1C,    /* Enter on main keyboard */
            DIK_LCONTROL = 0x1D,
            DIK_A = 0x1E,
            DIK_S = 0x1F,
            DIK_D = 0x20,
            DIK_F = 0x21,
            DIK_G = 0x22,
            DIK_H = 0x23,
            DIK_J = 0x24,
            DIK_K = 0x25,
            DIK_L = 0x26,
            DIK_SEMICOLON = 0x27,
            DIK_APOSTROPHE = 0x28,
            DIK_GRAVE = 0x29,    /* accent grave */
            DIK_LSHIFT = 0x2A,
            DIK_BACKSLASH = 0x2B,
            DIK_Z = 0x2C,
            DIK_X = 0x2D,
            DIK_C = 0x2E,
            DIK_V = 0x2F,
            DIK_B = 0x30,
            DIK_N = 0x31,
            DIK_M = 0x32,
            DIK_COMMA = 0x33,
            DIK_PERIOD = 0x34,    /* . on main keyboard */
            DIK_SLASH = 0x35,    /* / on main keyboard */
            DIK_RSHIFT = 0x36,
            DIK_MULTIPLY = 0x37,    /* * on numeric keypad */
            DIK_LMENU = 0x38,    /* left Alt */
            DIK_SPACE = 0x39,
            DIK_CAPITAL = 0x3A,
            DIK_F1 = 0x3B,
            DIK_F2 = 0x3C,
            DIK_F3 = 0x3D,
            DIK_F4 = 0x3E,
            DIK_F5 = 0x3F,
            DIK_F6 = 0x40,
            DIK_F7 = 0x41,
            DIK_F8 = 0x42,
            DIK_F9 = 0x43,
            DIK_F10 = 0x44,
            DIK_NUMLOCK = 0x45,
            DIK_SCROLL = 0x46,    /* Scroll Lock */
            DIK_NUMPAD7 = 0x47,
            DIK_NUMPAD8 = 0x48,
            DIK_NUMPAD9 = 0x49,
            DIK_SUBTRACT = 0x4A,    /* - on numeric keypad */
            DIK_NUMPAD4 = 0x4B,
            DIK_NUMPAD5 = 0x4C,
            DIK_NUMPAD6 = 0x4D,
            DIK_ADD = 0x4E,    /* + on numeric keypad */
            DIK_NUMPAD1 = 0x4F,
            DIK_NUMPAD2 = 0x50,
            DIK_NUMPAD3 = 0x51,
            DIK_NUMPAD0 = 0x52,
            DIK_DECIMAL = 0x53,    /* . on numeric keypad */
            DIK_OEM_102 = 0x56,    /* <> or \| on RT 102-key keyboard (Non-U.S.) */
            DIK_F11 = 0x57,
            DIK_F12 = 0x58,
            DIK_F13 = 0x64,    /*                     (NEC PC98) */
            DIK_F14 = 0x65,    /*                     (NEC PC98) */
            DIK_F15 = 0x66,    /*                     (NEC PC98) */
            DIK_KANA = 0x70,    /* (Japanese keyboard)            */
            DIK_ABNT_C1 = 0x73,    /* /? on Brazilian keyboard */
            DIK_CONVERT = 0x79,    /* (Japanese keyboard)            */
            DIK_NOCONVERT = 0x7B,    /* (Japanese keyboard)            */
            DIK_YEN = 0x7D,    /* (Japanese keyboard)            */
            DIK_ABNT_C2 = 0x7E,    /* Numpad . on Brazilian keyboard */
            DIK_NUMPADEQUALS = 0x8D,    /* = on numeric keypad (NEC PC98) */
            DIK_PREVTRACK = 0x90,    /* Previous Track (DIK_CIRCUMFLEX on Japanese keyboard) */
            DIK_AT = 0x91,    /*                     (NEC PC98) */
            DIK_COLON = 0x92,    /*                     (NEC PC98) */
            DIK_UNDERLINE = 0x93,    /*                     (NEC PC98) */
            DIK_KANJI = 0x94,    /* (Japanese keyboard)            */
            DIK_STOP = 0x95,    /*                     (NEC PC98) */
            DIK_AX = 0x96,    /*                     (Japan AX) */
            DIK_UNLABELED = 0x97,    /*                        (J3100) */
            DIK_NEXTTRACK = 0x99,    /* Next Track */
            DIK_NUMPADENTER = 0x9C,    /* Enter on numeric keypad */
            DIK_RCONTROL = 0x9D,
            DIK_MUTE = 0xA0,    /* Mute */
            DIK_CALCULATOR = 0xA1,    /* Calculator */
            DIK_PLAYPAUSE = 0xA2,    /* Play / Pause */
            DIK_MEDIASTOP = 0xA4,    /* Media Stop */
            DIK_VOLUMEDOWN = 0xAE,    /* Volume - */
            DIK_VOLUMEUP = 0xB0,    /* Volume + */
            DIK_WEBHOME = 0xB2,    /* Web home */
            DIK_NUMPADCOMMA = 0xB3,    /* , on numeric keypad (NEC PC98) */
            DIK_DIVIDE = 0xB5,    /* / on numeric keypad */
            DIK_SYSRQ = 0xB7,
            DIK_RMENU = 0xB8,    /* right Alt */
            DIK_PAUSE = 0xC5,    /* Pause */
            DIK_HOME = 0xC7,    /* Home on arrow keypad */
            DIK_UP = 0xC8,    /* UpArrow on arrow keypad */
            DIK_PRIOR = 0xC9,    /* PgUp on arrow keypad */
            DIK_LEFT = 0xCB,    /* LeftArrow on arrow keypad */
            DIK_RIGHT = 0xCD,    /* RightArrow on arrow keypad */
            DIK_END = 0xCF,    /* End on arrow keypad */
            DIK_DOWN = 0xD0,    /* DownArrow on arrow keypad */
            DIK_NEXT = 0xD1,    /* PgDn on arrow keypad */
            DIK_INSERT = 0xD2,    /* Insert on arrow keypad */
            DIK_DELETE = 0xD3,    /* Delete on arrow keypad */
            DIK_LWIN = 0xDB,    /* Left Windows key */
            DIK_RWIN = 0xDC,    /* Right Windows key */
            DIK_APPS = 0xDD,    /* AppMenu key */
            DIK_POWER = 0xDE,    /* System Power */
            DIK_SLEEP = 0xDF,    /* System Sleep */
            DIK_WAKE = 0xE3,    /* System Wake */
            DIK_WEBSEARCH = 0xE5,    /* Web Search */
            DIK_WEBFAVORITES = 0xE6,    /* Web Favorites */
            DIK_WEBREFRESH = 0xE7,    /* Web Refresh */
            DIK_WEBSTOP = 0xE8,    /* Web Stop */
            DIK_WEBFORWARD = 0xE9,    /* Web Forward */
            DIK_WEBBACK = 0xEA,    /* Web Back */
            DIK_MYCOMPUTER = 0xEB,    /* My Computer */
            DIK_MAIL = 0xEC,    /* Mail */
            DIK_MEDIASELECT = 0xED    /* Media Select */
        }
        #endregion
    }
}
